package com.roy.spring.service;

import com.roy.spring.entity.BaseRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.Serializable;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 第一个是Entity类型， 第二个是Repository类型
 * Created by admin on 18-1-13.
 */

public class BaseService<E, R extends BaseRepository> {

    private R repo;

    public BaseService(R repository) {
        this.repo = repository;
    }

    public Page<E> find(Map<String, Object> paramsMap) {
        Sort sort = null;
        String orderBy = (String) paramsMap.get("sort");
        if(StringUtils.hasText(orderBy)) {
            if (orderBy.startsWith("-")) {
                sort = new Sort(Sort.Direction.DESC, orderBy);
            } else {
                sort = new Sort(Sort.Direction.ASC);
            }
        }
        Integer pageNo = (Integer)paramsMap.get("pageNo");
        Integer pageSize = (Integer)paramsMap.get("pageSize");

        //Pageable pageable = new PageRequest(pageNo, pageSize, sort);
        paramsMap.remove("pageNo");
        paramsMap.remove("pageSize");
        paramsMap.remove("sort");

        return this.find(paramsMap, orderBy, pageNo, pageSize);
    }


    /**
     * 这里支持排序功能:
     * <pre>
     *     -name: 表示按照name倒倒叙排列
     *     +createTime: 表示按照createTime顺序排序，+ 可以省略
     *     -name,createTime: 表示先按照name倒叙，再按照createTime顺序排，sql: order by name desc, create_time
     * </pre>
     * @param paramsMap
     * @param sortField
     * @param page
     * @param size
     * @return
     */
    public Page<E> find(Map<String, Object> paramsMap, String sortField, int page, int size) {

        Sort sort = null;
        if(StringUtils.hasText(sortField)) {
            if (sortField.startsWith("-")) {
                sort = new Sort(Sort.Direction.DESC, sortField);
            }
        }
        //If sort is null
        Pageable pageable = PageRequest.of(page, size);
        //If sort is not null
        //pageable = PageRequest.of(page,size, sort);

        Specification querySpec = new Specification<E>() {
            @Override
            public Predicate toPredicate(Root<E> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//                cb.lessThan(root.get)
                List<Predicate> queryConditions = new LinkedList<>();
                paramsMap.forEach((k, v) -> {
                    if (k.startsWith("_seq_")) {
                        String fieldName = k.replace("_seq_", "");
                        queryConditions.add(cb.equal(root.get(fieldName), v));
                    }
                    if (k.startsWith("_dgt_")) {
                        String fieldName = k.replace("_dgt_", "");
                        queryConditions.add(cb.greaterThan(root.get(fieldName), (Date) v));
                    }
                    if (k.startsWith("_dlt_")) {
                        String fieldName = k.replace("_dlt_", "");
                        queryConditions.add(cb.lessThan(root.get(fieldName), (Date) v));
                    }
                });
                return cb.and(queryConditions.toArray(new Predicate[0]));
            }
        };

        return this.repo.findAll(querySpec, pageable);
    }

    public E findOne(Map<String, Object> paramsMap, String sort) {
        Page<E> pageData = this.find(paramsMap, sort, 0, 1);
        if (pageData != null && !CollectionUtils.isEmpty(pageData.getContent())) {
            return pageData.getContent().get(0);
        }
        return null;
    }

    public E save(E entity) {
        return (E)repo.save(entity);
    }

    public E update(E entity) {
        return (E)repo.save(entity);
    }

    public void delete(Serializable id) {
        repo.delete(id);
    }

    public R getRepo() {
        return repo;
    }

}
